Skip to content

fix: strip governance model prefix from raw body in Anthropic passthrough#1866

Open
Edward-Upton wants to merge 1 commit intomaximhq:mainfrom
Edward-Upton:ed/fix-count-tokens-model-prefix
Open

fix: strip governance model prefix from raw body in Anthropic passthrough#1866
Edward-Upton wants to merge 1 commit intomaximhq:mainfrom
Edward-Upton:ed/fix-count-tokens-model-prefix

Conversation

@Edward-Upton
Copy link
Contributor

@Edward-Upton Edward-Upton commented Mar 2, 2026

Summary

  • Fix 404 errors on Anthropic count_tokens (and other) requests when the governance plugin is active with passthrough mode
  • The governance plugin's load balancer prepends anthropic/ to the model name in the request body for routing, but checkAnthropicPassthrough only stripped the prefix from the parsed struct—not from the raw HTTP body used by passthrough mode
  • Anthropic's API rejects the prefixed model name ("anthropic/claude-sonnet-4-6") → 404

Changes

  1. anthropic.go: Added stripModelPrefixFromBody() using sonic's lazy AST (ast.NewRaw + root.Get + root.Set + root.MarshalJSON) to replace the governance-prefixed model with the bare model name. Only the "model" field is parsed and re-serialized; the rest of the body is copied as raw bytes — avoiding a full Unmarshal/Marshal round-trip on potentially large LLM request bodies.

  2. router.go: Changed SetRawRequestBody(rawBody) to SetRawRequestBody(ctx.Request.Body()) so the passthrough path picks up the body corrected by PreCallback instead of the stale pre-callback snapshot.

  3. anthropic_test.go: Added 7 unit tests for stripModelPrefixFromBody covering: prefix stripping, field preservation, non-matching model, missing model field, empty body, invalid JSON, and nested content safety (model name appearing inside message content is not modified).

Conditions for the bug

All three must be true simultaneously:

  1. Governance plugin active with a virtual key (triggers loadBalanceProvider which adds provider prefix)
  2. Passthrough mode enabled (Claude Code request detected → BifrostContextKeyUseRawRequestBody = true)
  3. Request goes through an Anthropic integration route (/anthropic/v1/messages/count_tokens, etc.)

Test plan

  • Verified the build compiles (go build ./bifrost-http/integrations/...)
  • Unit tests for stripModelPrefixFromBody (7 cases, all passing)
  • Existing integration tests pass (go test ./bifrost-http/integrations/...)
  • Test with Anthropic count_tokens endpoint through governance + passthrough
  • Verify regular responses and responses_stream requests still work with passthrough
  • Verify non-passthrough requests are unaffected

Made with Cursor

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

This PR adds model prefix stripping functionality for Anthropic HTTP request bodies during passthrough handling and updates the request routing logic to ensure body modifications from pre-processing callbacks are captured and forwarded upstream to Bifrost.

Changes

Cohort / File(s) Summary
Anthropic Passthrough Enhancement
transports/bifrost-http/integrations/anthropic.go
Added stripModelPrefixFromBody helper function that parses HTTP request JSON bodies and replaces prefixed model names with their bare equivalents. Integrated into both AnthropicTextRequest and AnthropicMessageRequest passthrough paths. Note: Function appears duplicated in diff.
Request Body Handling
transports/bifrost-http/integrations/router.go
Modified raw request body handling to re-read the body from the current fasthttp context instead of using a previously captured version, ensuring that body modifications from PreCallback hooks (such as model prefix stripping) are properly reflected before transmission to Bifrost.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through JSON streams,
Stripping prefixes from model dreams,
Each passthrough request now runs so clean,
With bodies re-read in between!
Fresh logic bounces to Bifrost's door,
Making prefix troubles no more! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: fixing the governance model prefix stripping issue in the Anthropic passthrough code path.
Description check ✅ Passed The PR description covers all major template sections: clear summary, detailed changes with design decisions, affected areas identified, test plan documented, and related sections completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
transports/bifrost-http/integrations/anthropic.go (1)

277-301: Consider logging on marshal failure for debuggability.

The function correctly handles the model prefix stripping. However, if sonic.Unmarshal succeeds but sonic.Marshal fails (line 297-299), the request proceeds with the prefixed model name still in the raw body, potentially causing the original 404 issue. While this edge case is rare, a debug log would help diagnose such issues.

💡 Optional: Add debug logging on marshal failure
 	payload["model"] = bareModel
 	newBody, err := sonic.Marshal(payload)
 	if err != nil {
+		// Note: Request will proceed with prefixed model in raw body
 		return
 	}
 	ctx.Request.SetBody(newBody)
 }

Alternatively, if a logger is accessible in this context, consider logging a warning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transports/bifrost-http/integrations/anthropic.go` around lines 277 - 301,
stripModelPrefixFromBody currently swallows errors from sonic.Marshal, so when
Marshal fails the request keeps the prefixed model and can 404; update
stripModelPrefixFromBody to log the marshal error (include the error text and
context: prefixedModel, bareModel, and maybe request id from ctx if available)
before returning, using the package's existing logger if accessible, otherwise
use a debug/warn-friendly logging helper; ensure the log call is placed right
where sonic.Marshal fails (after the err check) and do not change the current
control flow beyond adding the log.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@transports/bifrost-http/integrations/anthropic.go`:
- Around line 277-301: stripModelPrefixFromBody currently swallows errors from
sonic.Marshal, so when Marshal fails the request keeps the prefixed model and
can 404; update stripModelPrefixFromBody to log the marshal error (include the
error text and context: prefixedModel, bareModel, and maybe request id from ctx
if available) before returning, using the package's existing logger if
accessible, otherwise use a debug/warn-friendly logging helper; ensure the log
call is placed right where sonic.Marshal fails (after the err check) and do not
change the current control flow beyond adding the log.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80b70fb and c5ebf4f.

📒 Files selected for processing (2)
  • transports/bifrost-http/integrations/anthropic.go
  • transports/bifrost-http/integrations/router.go

…ough

The governance plugin's load balancer prepends the provider name to the
model field in the request body (e.g. "anthropic/claude-sonnet-4-6") for
internal routing. checkAnthropicPassthrough correctly strips this prefix
from the parsed struct, but the raw HTTP body—used by passthrough mode
to forward requests directly to Anthropic—still contained the prefixed
model name. This caused 404 errors from Anthropic's API (particularly
visible on count_tokens requests).

Fix: strip the provider prefix from the raw body in
checkAnthropicPassthrough, and re-read the body in the router after
PreCallback runs so the passthrough path gets the corrected model.

Made-with: Cursor
@Edward-Upton Edward-Upton force-pushed the ed/fix-count-tokens-model-prefix branch from c5ebf4f to 1bddf52 Compare March 5, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant